/*****************************************************************************

*This software is developed within IEEE 1857.9 subgroup for the reference test model of Immersive Video Content Coding.
*Contributor: Yueming Wang, wymmail@foxmail.com, Peking University Shenzhen Graduate School

*****************************************************************************/

#include <stdlib.h>
//#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include "stdafx.h"
#include "global.h"
#include "getopt.h"


////////////////////////////////////
#if ISP
void freemem(sourceInfo *source_Info)
{
	int i, j;
	int nFaces;
	nFaces = source_Info->face_number;
	if (source_Info->m_pFacesBuf)
	{
		for (i = 0; i < MAX_NUM_COMPONENT; i++)
		{

			if (source_Info->m_pFacesBuf[i])
			{
				for (j = 0; j < nFaces; j++)
				{
					if (source_Info->m_pFacesBuf[i][j])
					{
						free(source_Info->m_pFacesBuf[i][j]);
						source_Info->m_pFacesBuf[i][j] = NULL;
					}
				}
				free(source_Info->m_pFacesBuf[i]);
				source_Info->m_pFacesBuf[i] = NULL;
			}
		}
		free(source_Info->m_pFacesBuf);
		source_Info->m_pFacesBuf = NULL;
	}
	if (source_Info->m_pFacesOrig)
	{
		for (i = 0; i < MAX_NUM_COMPONENT; i++)
		{
			if (source_Info->m_pFacesOrig[i])
			{
				free(source_Info->m_pFacesOrig[i]);
				source_Info->m_pFacesOrig[i] = NULL;
			}
		}
		free(source_Info->m_pFacesOrig);
		source_Info->m_pFacesOrig = NULL;
	}

}
#endif
//********************************************************************
//Read one frame from yuv file
//********************************************************************
int Read_one_frame(FILE *file, unsigned char * src_y, int size)
{
  int read_size = 0;
  int bytes_y = size;
  int bytes_uv = size >> 2;

  unsigned char *src_u = src_y + bytes_y;
  unsigned char *src_v = src_u + bytes_uv;

  read_size += fread(src_y, 1, bytes_y, file);
  read_size += fread(src_u, 1, bytes_uv, file);
  read_size += fread(src_v, 1, bytes_uv, file);

  //If the data is not enough, return 0
  if (read_size != (size_t)(size * 3 / 2)) {
    printf("Read data size is not correct!\n");
    return 0;
  }

  return 1;
}

//********************************************************************
//write one frame to yuv file
//********************************************************************
int Write_one_frame(FILE *file, unsigned char * src_y, int size)
{
  int bytes_y = size;
  int bytes_uv = bytes_y >> 2;

  fwrite(src_y, (bytes_y + 2 * bytes_uv), 1, file);

  return 1;
}

coordinate * Read_sphere_file(FILE *file, int num){
  int i;
  coordinate * shpere = (coordinate*)malloc(sizeof(coordinate)*num);
  if (shpere == NULL) { printf("Malloc error: shpere\n"); return NULL; }
  for (i = 0; i < num; i++){
    fscanf(file, "%f %f", &shpere[i].latitude, &shpere[i].longitude);
  }
  return shpere;
}

int help_info(){
  printf("-i:     Input file name                               required\n");
  printf("-o:     Output file name                              required\n");
  printf("-w:     Input file width                              required\n");
  printf("-h:     Input file height                             required\n");
  printf("-x:     Output file width                             required\n");
  printf("-y:     Output file height                            required\n");
  printf("-N:     Number of frames                                      \n");
  printf("-M:     [map] or [psnr]                               required\n");
  printf("-s:     sphere file(Used for calculation S-PSNR)              \n");
  printf("-F:     input format, [eqar] or [cube] or [polerect] or [doublering] or [isp] or [asyerp] or [asc] or [ssCube]              required\n");
  printf("-f:     output format, [eqar] or [cube] or [polerect] or [doublering] or [isp] or [asyerp] or [asc] or [ssCube]             required");
  printf("-C:     input config file                                     \n");
  printf("-c:     output config file                                    \n");
  printf("cube ,asc, asyerp, ssCube need config file !!!!               \n");
  return -1;
}

int main(int argc, char** argv)
{
  //Read command line information
  long long FrameNoInFile = 0;//Frame Counter
  int flag = 0;   //1: map  2:psnr  0: error
  int flag_psnr = 0;  //1:psnr   2:spsnr   3:psnr and spsnr   0:none psnr
  int c;
  int sphere_num = 0;
  coordinate * sphere_point = NULL;
  char * type = 0, *sphere = NULL;
  FILE *sphere_file = NULL;
  float psnr[3] = { 0 };
  float all_psnr[3] = {0};
  float spsnr, all_spsnr = 0;

  sourceInfo input_Info, output_Info;
  configureInfo cfi_in, cfi_out;
  
  char *config_in = NULL;
  char *config_out = NULL;
  char *content;

  memset(&input_Info, 0x0, sizeof(sourceInfo));
  memset(&output_Info, 0x0, sizeof(sourceInfo));
  memset(&cfi_in, 0x0, sizeof(configureInfo));
  memset(&cfi_out, 0x0, sizeof(configureInfo));

  while ((c = getopt(argc, argv, "i:o:w:h:x:y:N:M:s:F:f:C:c:R:Y:P:A:T:")) != -1){
    switch (c){
	case 'i': input_Info.file_name	= optarg;                         break;
	case 'o': output_Info.file_name = optarg;                         break;
	case 'w': input_Info.width		= (int)strtol(optarg, 0, 0);      break;
	case 'h': input_Info.height		= (int)strtol(optarg, 0, 0);      break;
	case 'x': output_Info.width		= (int)strtol(optarg, 0, 0);      break;
	case 'y': output_Info.height	= (int)strtol(optarg, 0, 0);      break;
	case 'N': input_Info.frame_nums = (int)strtol(optarg, 0, 0);      break;
	case 'M': type					= optarg;                         break;
	case 's': sphere				= optarg;                         break;
	case 'F': input_Info.format		= optarg;                         break;
	case 'f': output_Info.format	= optarg;                         break;
	case 'C': config_in				= optarg;                         break;
	case 'c': config_out			= optarg;                         break;
    default: printf("Wrong input argument!\n"); return help_info();
    }
  }

  //check input and output format
  if (strcmp(input_Info.format, "eqar") && strcmp(input_Info.format, "cube") && strcmp(input_Info.format, "asyerp") && strcmp(input_Info.format, "doublering") && 
	  strcmp(input_Info.format, "polerect") && strcmp(input_Info.format, "asc") && strcmp(input_Info.format, "isp") && strcmp(input_Info.format, "ssCube")){
	  printf("Wrong input format!\n");
	  printf("-F:     input format, [eqar] or [cube] or [polerect] or [doublering] or [isp] or [asyerp] or [asc] or [ssCube]");
      return help_info();
  }

   if (strcmp(output_Info.format, "eqar") && strcmp(output_Info.format, "cube") && strcmp(output_Info.format, "asyerp") && strcmp(output_Info.format, "doublering") && 
	  strcmp(output_Info.format, "polerect") && strcmp(output_Info.format, "asc") && strcmp(output_Info.format, "isp") && strcmp(output_Info.format, "ssCube")){
	  printf("Wrong input format!\n");
	  printf("-f:     output format, [eqar] or [cube] or [polerect] or [doublering] or [isp] or [asyerp] or [asc] or [ssCube]");
      return help_info();
  }

  init_function_init(&input_Info, &output_Info);
  init_function_map(&input_Info, &output_Info);
  init_function_filter(&input_Info, &output_Info);
  init_function_psnr(&input_Info, &output_Info);
  init_function_cfi(&input_Info, &output_Info);
  init_function_post_tread(&input_Info, &output_Info);
  init_function_pre_tread(&input_Info, &output_Info);

  handle_function.init_lanczos_filter();

  if (!strcmp(type, "map")) {
    flag = 1;
  }  else if (!strcmp(type, "psnr")) {
	flag = 2;
  } else {
    printf("Please select map or psnr!\n"); 
    return help_info();
  }

  //Check whether the input is valid
  if ((input_Info.width <= 0) || (input_Info.height <= 0) || (output_Info.width <= 0) || (output_Info.height <= 0)) {
    printf("Wrong input width or height or output width or height!\n");
    return help_info();
  }
  if (input_Info.file_name == NULL || output_Info.file_name == NULL) {
    printf("Wrong input or output file name!\n");
    return help_info();
  }

  flag_psnr = 0;
  if (flag == 2) {
	if (input_Info.width == output_Info.width && input_Info.height == output_Info.height && !strcmp(input_Info.format, output_Info.format)) flag_psnr = flag_psnr | 0x1;
	if (sphere != NULL) flag_psnr = flag_psnr | 0x2;
  }
  if ((flag == 2) && flag_psnr == 0) {
    printf("The parameter for psnr calcalation is wrong!\n");
    return help_info();
  }

  //open input yuv file
  input_Info.file = fopen(input_Info.file_name, "rb");
  if (input_Info.file == NULL) {
	fprintf(stderr, "Opening input file %s error.\n", input_Info.file_name);
	goto errors;
  }

  //creat output yuv file if necessary
  if (flag == 1)
	output_Info.file = fopen(output_Info.file_name, "wb");
  else if (flag == 2)
	output_Info.file = fopen(output_Info.file_name, "rb");
  if (output_Info.file == NULL) {
	fprintf(stderr, "Creating output file %s error.\n", output_Info.file_name);
    goto errors;
  }


  if (sphere != NULL){
	int len = strlen(sphere);
    sphere_file = fopen(sphere, "r");
	while(len--){
		if(sphere[len] == '_') {sphere_num = atol(sphere + len + 1);break;}
	}
    if (sphere_file == NULL) {
      fprintf(stderr, "Opening sphere file %s error.\n", sphere);
      flag_psnr = flag_psnr & 0x1;
    }
	else if(sphere_num == 0){
		fprintf(stderr, "Reading sphere number error.\n");
		fprintf(stderr, "sphere file naming patterns: 'sphere(or other word)' + '_' + 'number'('number' mean the number of points to calculate the SPSNR).\n");
		flag_psnr = flag_psnr & 0x1;
	}
  }

  if (config_in) {
	  content = handle_function.GetConfigFileContent_in(config_in);
	  handle_function.ParseContent_in(content, strlen(content), &cfi_in);
  }

  if (config_out) {
	  content = handle_function.GetConfigFileContent_out(config_out);
	  handle_function.ParseContent_out(content, strlen(content), &cfi_out);
  }

  printf("%s to %s\n", input_Info.file_name, output_Info.file_name);
  //********************************************************************
  //map or invmap
  //********************************************************************
  if (flag == 1){
#if MTK_ORIENTATION    
    //auto-orientation
    if (cfi_in.orientation_detection && !strcmp (input_Info.format,"eqar") && !strcmp (output_Info.format,"eqar"))
      content_orientation(&input_Info, &cfi_in);
#endif
    //map
    
    //initialization buffer
    handle_function.init_in(&input_Info, &cfi_in, 1);
    handle_function.init_out(&output_Info, &cfi_out, 0);
    printf("Frame %lld", 0);
    while (FrameNoInFile < input_Info.frame_nums)
    {
      printf("Frame %lld", FrameNoInFile);
      if (!Read_one_frame(input_Info.file, input_Info.buf_y, input_Info.size_y)) break;

      handle_function.map_one_frame(&input_Info, &output_Info, &cfi_in, &cfi_out);

      Write_one_frame(output_Info.file, output_Info.buf_y, output_Info.size_y);

      FrameNoInFile++;
    }
    printf("\n");
  }
  else if (flag == 2){
    //calculate psnr

    //initialization
	handle_function.init_in(&input_Info, &cfi_in, 1);
	handle_function.init_out(&output_Info, &cfi_out, 1);

    if (flag_psnr & 0x2){
      sphere_point = Read_sphere_file(sphere_file, sphere_num);
      if (sphere_point == NULL) {
        printf("sphere_point is NULL\n");
        goto errors;
      }
    }
	while (FrameNoInFile < input_Info.frame_nums){
	  if (!Read_one_frame(input_Info.file, input_Info.buf_y, input_Info.size_y)) break;
	  if (!Read_one_frame(output_Info.file, output_Info.buf_y, output_Info.size_y)) break;
	  if (flag_psnr & 0x1){
		  handle_function.calculation_psnr(&input_Info, &output_Info, &cfi_in, &cfi_out, psnr);
		  printf("Frame %lld PSNR:\t%7.4f, %7.4f, %7.4f\n", FrameNoInFile, psnr[0], psnr[1], psnr[2]);
		  all_psnr[0] += psnr[0];
		  all_psnr[1] += psnr[1];
		  all_psnr[2] += psnr[2];
	  }
      if (flag_psnr & 0x2){
		  handle_function.calculation_spsnr(&input_Info, &output_Info, &cfi_in, &cfi_out, sphere_point, sphere_num, &spsnr);
		  printf("Frame %lld S-PSNR:\t%7.4f\n", FrameNoInFile, spsnr);
          all_spsnr += spsnr;
      }
      FrameNoInFile++;
    }
    if (flag_psnr & 0x1){
      printf("\nAverage PSNR:\t%7.4f, %7.4f, %7.4f\n", all_psnr[0] / FrameNoInFile, all_psnr[1] / FrameNoInFile, all_psnr[2] / FrameNoInFile);
    }
    if (flag_psnr & 0x2){
      printf("Average S-PSNR:\t%7.4f\n", all_spsnr / FrameNoInFile);
    }
  }
  
  //close file and free buffer
  if (input_Info.file) fclose(input_Info.file);
  if (output_Info.file) fclose(output_Info.file);
  if (sphere_file) fclose(sphere_file);
  if (input_Info.buf_y) free(input_Info.buf_y);
  if (output_Info.buf_y) free(output_Info.buf_y);
#if ISP
  if (!strcmp(input_Info.format, "isp"))
  {
	  freemem(&input_Info);
  }
  if (!strcmp(output_Info.format, "isp"))
  {
	  freemem(&output_Info);
  }
#endif
  return 0;
  
errors:
  if (input_Info.file) fclose(input_Info.file);
  if (output_Info.file) fclose(output_Info.file);
  if (sphere_file) fclose(sphere_file);
  if (input_Info.buf_y) free(input_Info.buf_y);
  if (output_Info.buf_y) free(output_Info.buf_y);
#if ISP
  if (!strcmp(input_Info.format, "isp"))
  {
	  freemem(&input_Info);
  }
  if (!strcmp(output_Info.format, "isp"))
  {
	  freemem(&output_Info);
  }
#endif
  return -1;
}

